home *** CD-ROM | disk | FTP | other *** search
/ Chip 2007 January, February, March & April / Chip-Cover-CD-2007-02.iso / Pakiet bezpieczenstwa / mini Pentoo LiveCD 2006.1 / mpentoo-2006.1.iso / livecd.squashfs / usr / lib / metasploit / msfpescan < prev    next >
Text File  |  2006-06-30  |  12KB  |  520 lines

  1. #!/usr/bin/perl
  2. ###############
  3.  
  4. ##
  5. #         Name: msfpescan
  6. #       Author: H D Moore <hdm [at] metasploit.com>
  7. #      Version: $Revision: 1.34 $
  8. #  Description: Search PE files for given opcodes
  9. #      License:
  10. #
  11. #      This file is part of the Metasploit Exploit Framework
  12. #      and is subject to the same licenses and copyrights as
  13. #      the rest of this package.
  14. #
  15. ##
  16.  
  17. require 5.6.0;
  18.  
  19. use FindBin qw{$RealBin};
  20. use lib "$RealBin/lib";
  21. use Getopt::Std;
  22. use strict;
  23.  
  24. use Pex::PEInfo;
  25. use Pex;
  26. use Pex::Nasm::Ndisasm;
  27.  
  28. use Msf::ColPrint;
  29. use Msf::TextUI;
  30.  
  31. no utf8;
  32. no locale;
  33.  
  34. Msf::UI::ActiveStateSucks();
  35. Msf::UI::BrokenUTF8();
  36.  
  37. my $VERSION = '$Revision: 1.34 $';
  38.  
  39. my %opts = ();
  40. my %jmps =
  41.   (
  42.     "\xff\xd0" => ["eax", "call"],
  43.     "\xff\xe0" => ["eax", "jmp" ],
  44.     "\xff\xd1" => ["ecx", "call"],
  45.     "\xff\xe1" => ["ecx", "jmp" ],
  46.     "\xff\xd2" => ["edx", "call"],
  47.     "\xff\xe2" => ["edx", "jmp" ],
  48.     "\xff\xd3" => ["ebx", "call"],
  49.     "\xff\xe3" => ["ebx", "jmp" ],
  50.     "\xff\xe4" => ["esp", "jmp" ],
  51.     "\xff\xd5" => ["ebp", "call"],
  52.     "\xff\xe5" => ["ebp", "jmp" ],
  53.     "\xff\xd6" => ["esi", "call"],
  54.     "\xff\xe6" => ["esi", "jmp" ],
  55.     "\xff\xd7" => ["edi", "call"],
  56.     "\xff\xe7" => ["edi", "jmp" ],
  57.  
  58.     "\x50\xc3" => ["eax", "push"],
  59.     "\x53\xc3" => ["ebx", "push"],
  60.     "\x51\xc3" => ["ecx", "push"],
  61.     "\x52\xc3" => ["edx", "push"],
  62.     "\x54\xc3" => ["esp", "push"],
  63.     "\x55\xc3" => ["ebp", "push"],
  64.     "\x56\xc3" => ["esi", "push"],
  65.     "\x57\xc3" => ["edi", "push"],
  66.   );
  67.  
  68. my %pops =
  69.   (
  70.     "eax"   => "\x58",
  71.     "ebx"   => "\x5b",
  72.     "ecx"   => "\x59",
  73.     "edx"   => "\x5a",
  74.     "esi"   => "\x5e",
  75.     "edi"   => "\x5f",
  76.     "ebp"   => "\x5d",
  77.   );
  78.  
  79. getopts("f:d:j:sx:a:B:A:I:nhvDES", \%opts);
  80. Usage()   if($opts{'h'});
  81. Version() if($opts{'v'});
  82.  
  83. if ($opts{'h'} ||
  84.     (! defined($opts{'f'}) && ! defined($opts{'d'})) ||
  85.     (! defined($opts{'j'}) &&
  86.         ! defined($opts{'x'}) &&
  87.         ! defined($opts{'a'}) &&
  88.         ! defined($opts{'D'}) &&
  89.         ! defined($opts{'S'}) &&
  90.         ! $opts{'s'})
  91.   )
  92. {
  93.     Usage();
  94.     exit(0);
  95. }
  96.  
  97. my $func;
  98. my $args = { };
  99.  
  100. if(exists($opts{'s'})) {
  101.     $func = \&popPopRet;
  102. }
  103. elsif(exists($opts{'j'})) {
  104.     $func = \&jmpReg;
  105.     $args->{'reg'} = $opts{'j'};
  106. }
  107. elsif(exists($opts{'x'})) {
  108.     $func = \®ex;
  109.     $args->{'regex'} = $opts{'x'};
  110. }
  111. elsif(exists($opts{'a'})) {
  112.     $func = \&address;
  113.     $args->{'address'} = hex($opts{'a'});
  114. }
  115. elsif(exists($opts{'D'})) {
  116.     $func = \&dumpinfo;
  117.     $args->{'dumpinfo'} = hex($opts{'D'});
  118. }
  119. elsif(exists($opts{'S'})) {
  120.     $func = \&identify;
  121.     $args->{'identify'} = hex($opts{'S'});
  122. }
  123.  
  124. $args->{'before'} = $opts{'B'} if(exists($opts{'B'}));
  125. $args->{'after'} = $opts{'A'} if(exists($opts{'A'}));
  126.  
  127. if($opts{'f'}) {
  128.  
  129.     my $filename = $opts{'f'};
  130.     my $pe = Pex::PEInfo->new('File' => $filename, 'Debug' => $opts{'E'}, 'FullResources' => 1);
  131.  
  132.     if (! $pe)
  133.     {
  134.         print STDERR "$0: could not load PE image from file.\n";
  135.         exit(0);
  136.     }
  137.  
  138.     if ($opts{'I'}) { $pe->ImageBase($opts{'I'}) }
  139.  
  140.     &{$func}($pe, $args);
  141. }
  142. else {
  143.     my $dir = $opts{'d'};
  144.     opendir(INDIR, $dir);
  145.     my @files = readdir(INDIR);
  146.     closedir(INDIR);
  147.     foreach my $file (@files) {
  148.         if($file =~ /^(.{8})\.rng/) {
  149.  
  150.             #print "Good file: $dir $file\n";
  151.             my $pe = SkapeFoo->new($dir . '/' . $file, hex($1));
  152.             &{$func}($pe, $args);
  153.         }
  154.     }
  155. }
  156.  
  157. # Identify the binary based on signatures
  158. sub identify {
  159.     my $pe = shift;
  160.     my $data = $pe->Raw;
  161.     my $args = shift;
  162.     my $sigf = $RealBin.'/data/msfpescan/identify.txt';
  163.     my %sigs = ();
  164.  
  165.     my $ep       = $pe->VirtualToOffset($pe->EntryPoint + $pe->ImageBase);
  166.     my $filename = $pe->Filename;
  167.  
  168.  
  169.     if(! open(SIGS, "<" .$sigf)) {
  170.         print STDERR "[*] Could not load the signature database: $!\n";
  171.         return;
  172.     }
  173.  
  174.     # If ep_only is set, only scan from the endpoint offset
  175.     my $edata = substr($data, $ep, 8192);
  176.  
  177.     my $name;
  178.     my $regx;
  179.     my $ep_only = 0;
  180.     my $ep_off  = 0;
  181.     my $sidx = 0;
  182.  
  183.     # If the entrypoint was mangled, disable ep scanning
  184.     if (! length($edata)) {
  185.         $ep_off = 1;
  186.     }
  187.  
  188.     while (my $line = <SIGS>) {
  189.  
  190.         next if $line =~ /^\s*#/;
  191.         
  192.         if ($line =~ m/\[(.*)\]/) {
  193.             if ($name) {
  194.                 $sigs{$name} = [$regx, $ep_only];
  195.             }
  196.             $name = $1." [".$sidx++."]";
  197.             $ep_only = 0;
  198.             next;
  199.         }
  200.  
  201.         if ($line =~ m/signature\s*=\s*(.*)\s*/i) {
  202.             my $pattern = $1;
  203.             $regx = '';
  204.             foreach my $c (split(/\s+/, $pattern)) {
  205.                 if ($c eq '??') {
  206.                     $regx .= '.';
  207.                 }else{
  208.                     $regx .= "\\x".$c;
  209.                 }
  210.             }
  211.         }
  212.         if ($line =~ m/ep_only\s*=\s*T.*\s*/i) {
  213.             $ep_only = 1 if ! $ep_off;
  214.         }
  215.     }
  216.     if ($name) {
  217.         $sigs{$name} = [$regx, $ep_only];
  218.     }
  219.  
  220.     foreach my $sig (keys %sigs) {
  221.         my $regex = $sigs{$sig}->[0];
  222.         my @addrs= ();
  223.         
  224.         # Only compare the entry point?
  225.         if ($sigs{$sig}->[1]) {
  226.             
  227.             if ($edata =~ /^($regex)/) {
  228.                 push @addrs, $pe->EntryPoint + $pe->ImageBase;
  229.             }
  230.             
  231.         } else {
  232.             while($data =~ m/($regex)/g) {
  233.                 my $found = $1;
  234.                 my $index = pos($data) - length($found);
  235.                 my $va = $pe->OffsetToVirtual($index);
  236.                 push @addrs, $va if $va;
  237.             }
  238.         }
  239.         if (@addrs) {
  240.             print "$filename: $sig (".scalar(@addrs)." matches)\n";
  241.         }
  242.     }
  243. }
  244.  
  245. # Scan for pop/pop/ret addresses
  246. sub popPopRet
  247. {
  248.     my $pe = shift;
  249.     my $data = $pe->Raw;
  250.     my $args = shift;
  251.     foreach my $rA (keys(%pops))
  252.     {
  253.         foreach my $rB (keys(%pops))
  254.         {
  255.             my $opc = $pops{$rA} . $pops{$rB} . "\xc3";
  256.             my $lst = 0;
  257.             my $idx = index($data,  $opc, $lst);
  258.             while ($idx > 0)
  259.             {
  260.                 my $va = $pe->OffsetToVirtual($idx);
  261.                 printf("0x%.8x   $rA $rB ret\n", $va) if $va;
  262.                 $lst = $idx + 1;
  263.                 $idx = index($data, $opc, $lst);
  264.             }
  265.         }
  266.     }
  267. }
  268.  
  269. # Scan for jmp/call/push,ret addresses
  270. sub jmpReg
  271. {
  272.     my $pe = shift;
  273.     my $data = $pe->Raw;
  274.     my $args = shift;
  275.     my $reg = $args->{'reg'};
  276.     foreach my $opc (keys(%jmps))
  277.     {
  278.         next if ($reg && lc($reg) ne $jmps{$opc}->[0]);
  279.  
  280.         my $lst = 0;
  281.         my $idx = index($data, $opc, $lst);
  282.         while ($idx > 0)
  283.         {
  284.             my ($reg, $typ) = @{$jmps{$opc}};
  285.  
  286.             my $va = $pe->OffsetToVirtual($idx);
  287.             printf("0x%.8x   $typ $reg\n", $va) if $va;
  288.             $lst = $idx + 1;
  289.             $idx = index($data, $opc, $lst);
  290.         }
  291.     }
  292. }
  293.  
  294. # Regex
  295. sub regex {
  296.     my $pe = shift;
  297.     my $data = $pe->Raw;
  298.     my $args = shift;
  299.     my $regex = $args->{'regex'};
  300.     $regex .= '.' x $args->{'after'} if($args->{'after'});
  301.     $regex = ('.' x $args->{'before'}) . $regex if($args->{'before'});
  302.  
  303.     while($data =~ m/($regex)/g) {
  304.         my $found = $1;
  305.         my $index = pos($data) - length($found);
  306.         my $va = $pe->OffsetToVirtual($index);
  307.         printf("0x%.8x   %s\n", $va, hexOutput($found)) if $va;
  308.     }
  309. }
  310.  
  311. sub address {
  312.     my $pe = shift;
  313.     my $data = $pe->Raw;
  314.     my $args = shift;
  315.  
  316.     my $address = $args->{'address'} - $args->{'before'};
  317.     my $length = $args->{'before'} + $args->{'after'};
  318.     $length = 1 if(!$length);
  319.     my $index = $pe->VirtualToOffset($address);
  320.     my $found = substr($data, $index, $length);
  321.     return if(!defined($index) || length($found) == 0);
  322.     printf("0x%.8x   %s\n", $address, hexOutput($found));
  323. }
  324.  
  325. sub dumpinfo {
  326.     my $pe = shift;
  327.     my $args = shift;
  328.  
  329.     my @img_hdrs        = $pe->ImageHeaders;
  330.     my @opt_img_hdrs    = $pe->OptImageHeaders;
  331.     my $imports         = $pe->Imports;
  332.     my $exports         = $pe->Exports;
  333.     my $resources       = $pe->Resources;
  334.     my $version         = $pe->VersionStrings;
  335.     my $col;
  336.     my %setUEF          = ();
  337.     my $setUEF_IAT      = 0;
  338.     
  339.     print "\n\n[ Image Headers ]\n\n";
  340.     $col = Msf::ColPrint->new(4, 4);
  341.     foreach my $hdr (@img_hdrs) {
  342.         $col->AddRow($hdr, sprintf("0x%.8x",$pe->ImageHeader($hdr)));
  343.     }
  344.     print $col->GetOutput;
  345.  
  346.     print "\n\n[ Optional Headers ]\n\n";
  347.     $col = Msf::ColPrint->new(4, 4);
  348.     foreach my $hdr (@opt_img_hdrs) {
  349.         $col->AddRow($hdr, sprintf("0x%.8x",$pe->OptImageHeader($hdr)));
  350.     }
  351.     print $col->GetOutput;
  352.  
  353.     print "\n\n[ Exported Functions ]\n\n";
  354.     $col = Msf::ColPrint->new(4, 4);
  355.     foreach my $name (@{ $exports->{'ordinals'} }) {
  356.         my $add = $exports->{'funcs'}->{$name}->{'add'};
  357.         my $ord = $exports->{'funcs'}->{$name}->{'ord'};
  358.         next if ! $ord;
  359.         $col->AddRow($ord, $name, sprintf("0x%.8x",$add));
  360.     }
  361.     print $col->GetOutput;
  362.  
  363.     print "\n\n[ Imported Functions ]\n\n";
  364.     $col = Msf::ColPrint->new(4, 4);
  365.     foreach my $module (keys(%{ $imports })) {
  366.         foreach my $func (sort(keys(%{ $imports->{$module} }))) {
  367.             $col->AddRow($module, $func,
  368.                 "IAT ".sprintf("0x%.8x", $imports->{$module}->{$func}->{'iat'})
  369.               );
  370.             
  371.             if (lc($func) eq 'setunhandledexceptionfilter') {
  372.                 $setUEF_IAT = $imports->{$module}->{$func}->{'iat'};
  373.             }
  374.         }
  375.         $col->AddRow("", "", "");
  376.     }
  377.     print $col->GetOutput;
  378.  
  379.     print "\n[ Resources ]\n\n";
  380.     $col = Msf::ColPrint->new(4, 4);
  381.     foreach my $type (sort(keys(%{ $resources->{'Types'} }))) {
  382.         foreach my $name (sort(keys(%{ $resources->{'Types'}->{$type} }))) {
  383.             my $entry = $resources->{'Entries'}->{$name};
  384.             $col->AddRow($name, $entry->{'Name'}, "CP ".$entry->{'Code'}, $entry->{'Size'}." bytes");
  385.         }
  386.     }
  387.     print $col->GetOutput;
  388.  
  389.     print "\n\n[ Version Strings ]\n\n";
  390.     $col = Msf::ColPrint->new(4, 4);
  391.     foreach my $lang (keys(%{ $version })) {
  392.         foreach my $name (sort(keys(%{ $version->{$lang} }))) {
  393.             $col->AddRow($lang, $name, $version->{$lang}->{$name});
  394.         }
  395.     }
  396.     print $col->GetOutput;
  397.  
  398.  
  399.     return if not $setUEF_IAT;
  400.     
  401.     
  402.     print "\n\n[ SetUnhandledExceptionFilter ]\n\n";
  403.     $col = Msf::ColPrint->new(4, 4);
  404.  
  405.     my $data  = $pe->Raw;
  406.     my $regex = "(\x68|\xff\x15)".pack("V", $setUEF_IAT);
  407.     $regex .= '.' x $args->{'after'} if($args->{'after'});
  408.     $regex = ('.' x $args->{'before'}) . $regex if($args->{'before'});
  409.  
  410.     while($data =~ m/($regex)/g) {
  411.         my $found = $1;
  412.         my $index = pos($data) - length($found);
  413.         my $va = $pe->OffsetToVirtual($index);
  414.         if ($va) {
  415.             $col->AddRow(sprintf("0x%.8x", $va), hexOutput($found));
  416.         }
  417.     }
  418.     print $col->GetOutput;
  419. }
  420.  
  421. sub hexOutput {
  422.     my $data = shift;
  423.     my $string = unpack('H*', $data);
  424.     if($opts{'n'}) {
  425.  
  426.         #    my $tempString = $string;
  427.         #    $tempString =~ s/(..)/\\x$1/g;
  428.         $string .= "\n--- ndisasm output ---\n";
  429.  
  430.         #    $string .= `echo -ne "$tempString" | ndisasm -u /dev/stdin`;
  431.         $string .= Pex::Nasm::Ndisasm->DisasData($data);
  432.         $string .= "--- ndisasm output ---";
  433.     }
  434.     return($string);
  435. }
  436.  
  437. sub Usage
  438. {
  439.     print STDERR
  440.       qq{  Usage: $0 <input> <mode> <options>
  441. Inputs:
  442.          -f  <file>    Read in PE file
  443.          -d  <dir>     Process memdump output
  444. Modes:
  445.          -j  <reg>     Search for jump equivalent instructions
  446.          -s            Search for pop+pop+ret combinations
  447.          -x  <regex>   Search for regex match
  448.          -a  <address> Show code at specified virtual address
  449.          -D            Display detailed PE information
  450.          -S            Attempt to identify the packer/compiler
  451. Options:
  452.          -A  <count>   Number of bytes to show after match
  453.          -B  <count>   Number of bytes to show before match
  454.          -I  address   Specify an alternate ImageBase
  455.          -n            Print disassembly of matched data
  456.  
  457. };
  458.     exit(0);
  459.  
  460. }
  461. sub Version {
  462.     my $ver = Pex::Utils::Rev2Ver($VERSION);
  463.     print STDERR qq{
  464.    Msfpescan Version:  $ver 
  465.  
  466. };
  467.     exit(0);
  468. }
  469.  
  470. package SkapeFoo;
  471. use strict;
  472.  
  473. sub new {
  474.     my $class = shift;
  475.     my $self = bless({ }, $class);
  476.     $self->Filename(shift);
  477.     $self->Base(shift);
  478.     $self->ReadInRaw;
  479.     return($self);
  480. }
  481. sub Filename {
  482.     my $self = shift;
  483.     $self->{'Filename'} = shift if(@_);
  484.     return($self->{'Filename'});
  485. }
  486. sub Base {
  487.     my $self = shift;
  488.     $self->{'Base'} = shift if(@_);
  489.     return($self->{'Base'});
  490. }
  491. sub Raw {
  492.     my $self = shift;
  493.     $self->{'Raw'} = shift if(@_);
  494.     return($self->{'Raw'});
  495. }
  496.  
  497. sub ReadInRaw {
  498.     my $self = shift;
  499.     open(INFILE, '<' . $self->Filename) or return(0);
  500.     local $/;
  501.     my $data = <INFILE>;
  502.     close(INFILE);
  503.     $self->Raw($data);
  504.     return(1);
  505. }
  506.  
  507. sub VirtualToOffset {
  508.     my $self = shift;
  509.     my $virtual = shift;
  510.     return($virtual - $self->Base);
  511. }
  512.  
  513. sub OffsetToVirtual {
  514.     my $self = shift;
  515.     my $offset = shift;
  516.     return($offset + $self->Base);
  517. }
  518.  
  519. 1;
  520.